package com.csp.mingyue.auth.controller;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Handle;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
import cn.dev33.satoken.util.SaResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * OAuth2 Server 相关接口
 *
 * @author Strive
 * @date 2023/6/27 09:22
 */
@Tag(name = "OAuth2ServerController", description = "OAuth2 Server 相关接口")
@Slf4j
@RestController
@RequiredArgsConstructor
public class OAuth2ServerController {

	/**
	 * Sa-OAuth2 定制化配置
	 * @param cfg
	 */
	@Autowired
	public void setSaOAuth2Config(SaOAuth2Config cfg) {
		cfg.
		// 未登录的视图
				setNotLoginView(() -> new ModelAndView("login.html")).
				// 授权确认视图
				setConfirmView((clientId, scope) -> {
					Map<String, Object> map = new HashMap<>();
					map.put("clientId", clientId);
					map.put("scope", scope);
					return new ModelAndView("confirm.html", map);
				});
	}

	/**
	 * 统一认证地址
	 */
	@GetMapping("/oauth2/authorize")
	@Operation(summary = "统一认证地址")
	@Parameters({ @Parameter(name = "response_type", description = "返回类型：授权码（code）", required = true),
			@Parameter(name = "client_id", description = "应用id", required = true),
			@Parameter(name = "redirect_uri", description = "用户确认授权后，重定向的url地址", required = true),
			@Parameter(name = "scope", description = "具体请求的权限，多个用逗号隔开"),
			@Parameter(name = "state", description = "随机值，此参数会在重定向时追加到url末尾，不填不追加"), })
	public Object authorize() {
		log.info("------- 进入【统一认证地址】请求: " + SaHolder.getRequest().getUrl());
		return SaOAuth2Handle.authorize(SaHolder.getRequest(), SaHolder.getResponse(), SaOAuth2Manager.getConfig());
	}

	/**
	 * 确认授权接口
	 */
	@GetMapping("/oauth2/doConfirm")
	@Operation(summary = "确认授权接口")
	@Parameters({ @Parameter(name = "client_id", description = "应用id", required = true),
			@Parameter(name = "scope", description = "具体请求的权限，多个用逗号隔开") })
	public Object doConfirm() {
		log.info("------- 进入【确认授权接口】请求: " + SaHolder.getRequest().getUrl());
		return SaOAuth2Handle.doConfirm(SaHolder.getRequest());
	}

	/**
	 * 获取 Access-Token
	 */
	@GetMapping("/oauth2/token")
	@Operation(summary = "获取 Access-Token", description = "授权码模式、密码模式")
	@Parameters({ @Parameter(name = "grant_type", description = "授权类型，这里请填写：authorization_code", required = true),
			@Parameter(name = "client_id", description = "应用id", required = true),
			@Parameter(name = "client_secret", description = "应用秘钥", required = true),
			@Parameter(name = "code", description = "获取到的授权码") })
	public Object token() {
		log.info("------- 进入【获取 Access-Token】请求: " + SaHolder.getRequest().getUrl());
		return SaOAuth2Handle.token(SaHolder.getRequest(), SaHolder.getResponse(), SaOAuth2Manager.getConfig());
	}

	/**
	 * 刷新 Access-Token
	 */
	@GetMapping("/oauth2/refresh")
	@Operation(summary = "刷新 Access-Token")
	@Parameters({ @Parameter(name = "grant_type", description = "授权类型，这里请填写：refresh_token", required = true),
			@Parameter(name = "client_id", description = "应用id", required = true),
			@Parameter(name = "client_secret", description = "应用秘钥", required = true),
			@Parameter(name = "refresh_token", description = "获取到的 refresh_token 值") })
	public Object refresh() {
		log.info("------- 进入【刷新 Access-Token】请求: " + SaHolder.getRequest().getUrl());
		return SaOAuth2Handle.refreshToken(SaHolder.getRequest());
	}

	/**
	 * 回收 Access-Token
	 */
	@GetMapping("/oauth2/revoke")
	@Operation(summary = "回收 Access-Token")
	@Parameters({ @Parameter(name = "client_id", description = "应用id", required = true),
			@Parameter(name = "client_secret", description = "应用秘钥", required = true),
			@Parameter(name = "access_token", description = "获取到的 access_token 值") })
	public Object revoke() {
		log.info("------- 进入【回收 Access-Token】请求: " + SaHolder.getRequest().getUrl());
		return SaOAuth2Handle.revokeToken(SaHolder.getRequest());
	}

	@GetMapping("/oauth2/userinfo")
	@Operation(summary = "根据 Access-Token 获取相应用户的账号信息")
	@Parameters({ @Parameter(name = "access_token", description = "获取到的 access_token 值") })
	public SaResult userinfo() {
		log.info("------- 进入【获取相应用户的账号信息】请求: " + SaHolder.getRequest().getUrl());
		// 获取 Access-Token 对应的账号id
		String accessToken = SaHolder.getRequest().getParamNotNull("access_token");
		Object loginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken);
		log.info("-------- 此Access-Token对应的账号id: " + loginId);

		// 校验 Access-Token 是否具有权限: userinfo
		SaOAuth2Util.checkScope(accessToken, "userinfo");

		// 模拟账号信息 （真实环境需要查询数据库获取信息）
		Map<String, Object> map = new LinkedHashMap<>();
		map.put("nickname", "mingyue_");
		map.put("avatar", "http://xxx.com/1.jpg");
		map.put("age", "25");
		map.put("sex", "男");
		map.put("address", "江苏省 南京市 江宁区");
		return SaResult.data(map);
	}

}
